package net.esj.basic.dao.hibernate.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import net.esj.basic.core.engine.support.validator.exception.MelonValidateException;
import net.esj.basic.dao.Dao;
import net.esj.basic.dao.QueryBuilderDao;
import net.esj.basic.dao.ScalarMapper;
import net.esj.basic.dao.hibernate.HibernateDao;
import net.esj.basic.dao.jdbc.JdbcSqlUtils;
import net.esj.basic.pojo.AbstractPojo;
import net.esj.basic.pojo.UpdateType;
import net.esj.basic.utils.Pagination;
import net.esj.basic.utils.clone.ObjectWriteorRead;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Projections;
import org.hibernate.metadata.ClassMetadata;

public class HibernateDaoImpl extends HibernateQueryBuilderDaoImpl implements
		HibernateDao,Dao,QueryBuilderDao {

	
	public synchronized void delete(Object entity) {
		getSupportSource().delete(entity);
	}

	@Override
	public void deleteAll(Collection c) {
		getSupportSource().deleteAll(c);
	}

	@Override
	public void deleteByHql(String hql) {
		getSupportSource().getCurrentSession().createQuery(hql)	.executeUpdate();
	}

	@Override
	public <T> List<T> findAll(Class<T> clazz) {
		Criteria criteria = getSupportSource().getCurrentSession().createCriteria(clazz);
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		List list= criteria.list();
		getSession().evict(list);
		return list;
	}
	
	@Override
	public <T> List<T> findAll(Class<T> clazz, boolean cachable) {
		
		Criteria criteria = getSupportSource().getCurrentSession().createCriteria(clazz);
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		if(cachable){
			criteria.setCacheable(true);
		}
		List list= criteria.list();
		getSession().evict(list);
		return list;
	}

	public <T> List<T> findList(Object entity) {
		Criteria executableCriteria =  getSupportSource().getCurrentSession().createCriteria(entity.getClass());
		executableCriteria.add(Example.create(entity));
		List<T> list = executableCriteria.list();
		getSession().evict(list);
		return list;
	}


	@Override
	public <T> List<T> findTop(String hql, int count) {
		Query query = getSupportSource().getCurrentSession().createQuery(hql);
		query.setFirstResult(0);
		query.setMaxResults(count);
		List<T> list = query.list();
		getSession().evict(list);
		return list;
	}


	@Override
	public void flush() {
		getSupportSource().getCurrentSession().flush();
	}

	@Override
	public <T> T get(Serializable id, Class<T> clazz) {
		T t = (T) getSupportSource().get(id, clazz);
		return t;
	}
	
	@Override
	public <T> T load(Serializable id, Class<T> clazz) {
		return getSupportSource().load(id, clazz);
	}
	
	public <T> Criteria getCriteria(Class<T> clazz){
		return getSupportSource().getCurrentSession().createCriteria(clazz);
	}

	@Override
	public <T> Pagination<T> queryPage(String hql, Pagination<T> page) {
		if (page == null) {
			throw new IllegalArgumentException("page cannot be null!");
		} else {
			String newHql = JdbcSqlUtils.generateSqlNoOrder(hql);
			Query queryCount= getSupportSource().getCurrentSession().createQuery(newHql);
			queryCount(queryCount, page);

			Query query= getSupportSource().getCurrentSession().createQuery(hql);
			query.setFirstResult((page.getPage() - 1) * page.getRp());
			query.setMaxResults(page.getRp());
			List<T> list = query.list();
			getSession().evict(list);
			page.setRows(list);
		}
		return page;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> Pagination<T> queryPage(String sql, Pagination<T> page,ScalarMapper mapper) {
		if (page == null) {
			throw new IllegalArgumentException("page cannot be null!");
		} else {
			SQLQuery query= getSupportSource().getCurrentSession().createSQLQuery(sql);
			
			String str = generateSelectCountSql(query.getQueryString(),true);
			SQLQuery query2 = getSupportSource().getCurrentSession().createSQLQuery(str);
			query2.addScalar("COUNT", getSupportSource().INTEGER);
			List<Object[]> counts =query2.list();
			Iterator iterator = counts.iterator();
			int sum = 0;
			while (iterator.hasNext()) {
				sum = (Integer) iterator.next();
			}
			//总数量
			page.setTotal(sum);
			//总页数
			if(sum==0){
				page.setSum(0);
			}else{
				page.setSum((sum-1)/page.getRp()+1);
			}

			query.setFirstResult((page.getPage() - 1) * page.getRp());
			query.setMaxResults(page.getRp());
			
			List<T> list = mapper.execute(query);
			getSession().evict(list);
			page.setRows(list);
		}
		return page;
	}

	@Override
	public List queryList(String hql) {
		Query query= getSupportSource().getCurrentSession().createQuery(hql);
		List list = query.list();
		getSession().evict(list);
		return list;
	}

	@Override
	public List queryListBySql(String sql) {
		SQLQuery query= getSupportSource().getCurrentSession().createSQLQuery(sql);
		List list = query.list();
		getSession().evict(list);
		return list;
	}
	
	@Override
	public List queryList(String hql, ScalarMapper mapper) {
		SQLQuery query= getSupportSource().getCurrentSession().createSQLQuery(hql);
		List list = mapper.execute(query);
		getSession().evict(list);
		return list;
	}
	
	@Override
	public List queryTop(String hql, int count) {
		Query query= getSupportSource().getCurrentSession().createQuery(hql);
		query.setFirstResult(0);
		query.setMaxResults(count);
		List list = query.list();
		getSession().evict(list);
		return list;
	}

	@Override
	public List queryTop(String hql, int count, ScalarMapper mapper) {
		SQLQuery query= getSupportSource().getCurrentSession().createSQLQuery(hql);
		query.setFirstResult(0);
		query.setMaxResults(count);
		List list = mapper.execute(query);
		getSession().evict(list);
		return list;
	}

	
	@Override
	public void save(Object entity) throws MelonValidateException{
		getSupportSource().save(entity);
	}

	public void saveOrUpdate(Object entity) throws MelonValidateException{
		getSupportSource().saveOrUpdate(entity);
	}

	@Override
	public void update(Object entity) throws MelonValidateException{
		getSupportSource().update(entity);
	}
	
	protected void queryCount(Query query, Pagination page) {
		
		//ClassMetadata c= getClassMetadata(page.getEntityClass());
		
		String str = generateSelectCountSql(query.getQueryString(),false);
		List counts = getSupportSource().getCurrentSession().createQuery(str).list();
		Iterator iterator = counts.iterator();
		int sum = 0;
		while (iterator.hasNext()) {
			sum = ((Long) iterator.next()).intValue();
		}
		//总数量
		page.setTotal(sum);
		//总页数
		page.setSum((sum-1)/page.getRp()+1);
	}
	
	
	public static String generateSelectCountSql(String sql,boolean append){
		StringBuilder sb = new StringBuilder();
		if(append){
			sb.append("SELECT COUNT(*) AS COUNT FROM (").append(sql).append(") AS count_result ");
		}else{
			StringTokenizer st = new StringTokenizer(sql);
			int area = 0;//0:开始 1:SELECT 语句 2: FROM 语句
			while(st.hasMoreTokens()){
				switch(area){
				case 0:
					if("SELECT".equals(st.nextToken().toUpperCase())){
						sb.append("SELECT COUNT(*) ");
						area = 1;
					}
					break;
				case 1:
					if("FROM".equals(st.nextToken().toUpperCase())){
						sb.append("FROM");
						area = 2;
						break;
					}
					continue;
				case 2:
					sb.append(st.nextToken());
					break;
				}
				sb.append(" ");
			}
			return sb.toString();
		}
		return sb.toString();
	}

	public ClassMetadata getClassMetadata(Class clazz) {
        return (ClassMetadata) getSupportSource().getSessionFactory().getClassMetadata(clazz);
    }


	@Override
	public void update(Object entity, UpdateType type) {
		
		if(entity instanceof AbstractPojo){
			((AbstractPojo)entity).setUpdateType(type);
		}
		
		update(entity);
		
//		Session session = getSourceLocale().getCurrentSession();
//		SessionFactoryImpl fac = (SessionFactoryImpl) getSourceLocale().getSessionFactory();
//		Update update = new Update(fac.getDialect());
//		EntityPersister persister = fac.getEntityPersister(entity.getClass().getName());
//		//update.addColumn("", valueExpression)
//		persister.update(((PrimaryKey)entity).getId(), new Object[]{"asdsad",4}, new int[]{1}, true, null, null, entity, null, (SessionImplementor) getSourceLocale().getCurrentSession());
//		//update.setTableName(entity)
//		if(entity instanceof AbstractPojoRecoder){
//			((AbstractPojoRecoder)entity).notifyBackUp(updater);
//		}
	}

	public List listByCriteria(DetachedCriteria criteria){
		Criteria c = criteria.getExecutableCriteria(getSupportSource().getCurrentSession());
		return c.list();
	}


	@Override
	public Pagination pageByCriteria(DetachedCriteria dc, Pagination page) {
		
		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
		ObjectWriteorRead.writeObject(dc, byteOut);// 序列化对象, 这里需要用到对象拷贝，一直没有好的办法，用克隆的方法没效果，所有暂时也不考虑效率了，这里希望大家提点建议
		ByteArrayInputStream bytein = new ByteArrayInputStream(byteOut.toByteArray());
		DetachedCriteria newdc =  (DetachedCriteria) ObjectWriteorRead.readObject(bytein);
		
		Criteria criteria = dc.getExecutableCriteria(getSupportSource().getCurrentSession());
		
		int sum = countByCriteria(newdc);
		
		page.setTotal(sum);
		//总页数
		page.setSum((sum-1)/page.getRp()+1);
		
		criteria.setMaxResults(page.getRp());
		criteria.setFirstResult((page.getPage() - 1) * page.getRp());
		List list = criteria.list();
		page.setRows(list);
		
		return page;
	}


	@Override
	public List topByCriteria(DetachedCriteria criteria,int count) {
		Criteria c =  criteria.getExecutableCriteria(getSupportSource().getCurrentSession());
		c.setMaxResults(count);
		List list = c.list();
		getSession().evict(list);
		return list;
	}


	@Override
	public int countByCriteria(DetachedCriteria criteria) {
		criteria.setProjection(Projections.rowCount());
		Criteria c =  criteria.getExecutableCriteria(getSupportSource().getCurrentSession());
		List counts = c.list();
		Iterator iterator = counts.iterator();
		int sum = 0;
		while (iterator.hasNext()) {
			sum = Integer.valueOf( iterator.next().toString());
		}
		return sum;
	}

	@Override
	public void execute(String executeStr) {
		getSupportSource().getCurrentSession().createSQLQuery(executeStr).executeUpdate();
	}

	@Override
	public List query(String queryStr) {
		Query query= getSupportSource().getCurrentSession().createSQLQuery(queryStr);
		List list = query.list();
		getSession().evict(list);
		return list;
	}

}
